<?php

declare(strict_types=1);

namespace app\common\library;

use OSS\OssClient;
use OSS\Core\OssException;

class Oss
{
    /**
     * OSS客户端对象
     * @var OssClient
     */
    protected $client;

    /**
     * Bucket名称
     * @var string
     */
    protected $bucket;

    /**
     * OSS域名
     * @var string  
     */
    protected $domain;

    /**
     * 目录前缀
     * @var string
     */
    protected $prefix = '';

    /**
     * 构造函数
     * @throws OssException
     */
    public function __construct()
    {
        // 从环境变量获取配置
        $accessKeyId = env('OSS_ACCESS_KEY_ID', '');
        $accessKeySecret = env('OSS_ACCESS_KEY_SECRET', '');
        $endpoint = env('OSS_ENDPOINT', '');
        $this->bucket = env('OSS_BUCKET', '');
        $this->domain = env('OSS_DOMAIN', '');

        // 实例化OSS客户端
        $this->client = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
    }

    /**
     * 设置目录前缀
     * @param string $prefix
     * @return $this
     */
    public function setPrefix(string $prefix): self
    {
        $this->prefix = $prefix;
        return $this;
    }

    /**
     * 上传文件
     * @param string $filePath 本地文件路径
     * @param string $object OSS存储路径
     * @return string 文件访问URL
     * @throws OssException
     */
    public function uploadFile(string $filePath, string $object = ''): string
    {
        if (empty($object)) {
            // 使用原文件名
            $object = basename($filePath);
        }

        // 添加前缀
        if ($this->prefix) {
            $object = trim($this->prefix, '/') . '/' . ltrim($object, '/');
        }

        // 上传到OSS
        $this->client->uploadFile($this->bucket, $object, $filePath);

        // 返回文件URL
        return $this->getFileUrl($object);
    }

    /**
     * 上传文件内容
     * @param string $content 文件内容
     * @param string $object OSS存储路径
     * @return string 文件访问URL
     * @throws OssException
     */
    public function putObject(string $content, string $object): string
    {
        // 添加前缀
        if ($this->prefix) {
            $object = trim($this->prefix, '/') . '/' . ltrim($object, '/');
        }

        // 上传到OSS
        $this->client->putObject($this->bucket, $object, $content);

        // 返回文件URL
        return $this->getFileUrl($object);
    }

    /**
     * 删除文件
     * @param string $object OSS存储路径
     * @return bool
     */
    public function deleteObject(string $object): bool
    {
        try {
            // 添加前缀
            if ($this->prefix) {
                $object = trim($this->prefix, '/') . '/' . ltrim($object, '/');
            }

            $this->client->deleteObject($this->bucket, $object);
            return true;
        } catch (OssException $e) {
            return false;
        }
    }

    /**
     * 获取文件访问URL
     * @param string $object OSS存储路径
     * @param int $expireTime URL有效期(秒)，默认3600秒
     * @param array $options 处理参数
     * @return string
     */
    public function getFileUrl(string $object, int $expireTime = 3600, array $options = []): string
    {
        if ($this->isPrivate()) {
            return $this->client->signUrl($this->bucket, $object, $expireTime, 'GET', $options);
        } else {
            $endpoint = env('OSS_ENDPOINT', '');
            $url = 'https://' . $this->bucket . '.' . $endpoint . '/' . $object;
            
            // 如果有处理参数，添加到URL
            if (!empty($options)) {
                $process = $options['x-oss-process'] ?? '';
                if ($process) {
                    $url .= '?' . $process;
                }
            }
            
            return $url;
        }
    }

    /**
     * 判断是否为私有Bucket
     * @return bool
     */
    public function isPrivate(): bool
    {
        try {
            $acl = $this->client->getBucketAcl($this->bucket);
            return $acl == 'private';
        } catch (OssException $e) {
            return true; // 获取失败时默认为私有
        }
    }

    /**
     * 判断文件是否存在
     * @param string $object OSS存储路径
     * @return bool
     */
    public function doesObjectExist(string $object): bool
    {
        try {
            // 添加前缀
            if ($this->prefix) {
                $object = trim($this->prefix, '/') . '/' . ltrim($object, '/');
            }

            return $this->client->doesObjectExist($this->bucket, $object);
        } catch (OssException $e) {
            return false;
        }
    }

    /**
     * 获取文件元信息
     * @param string $object OSS存储路径
     * @return array|null
     */
    public function getObjectMeta(string $object): ?array
    {
        try {
            // 添加前缀
            if ($this->prefix) {
                $object = trim($this->prefix, '/') . '/' . ltrim($object, '/');
            }

            return $this->client->getObjectMeta($this->bucket, $object);
        } catch (OssException $e) {
            return null;
        }
    }

    /**
     * 获取文件列表
     * @param array $options 查询参数
     * @return \OSS\Model\ObjectListInfo
     * @throws OssException
     */
    public function listObjects(array $options = []): \OSS\Model\ObjectListInfo
    {
        // 设置默认参数
        $options = array_merge([
            'max-keys' => 100,    // 每页数量
            'marker' => '',       // 起始文件
            'prefix' => '',       // 目录前缀
            'delimiter' => '/'    // 分隔符，用于模拟目录结构
        ], $options);

        // 添加前缀
        if ($this->prefix && !isset($options['prefix'])) {
            $options['prefix'] = $this->prefix;
        }

        // 获取文件列表
        $result = $this->client->listObjects($this->bucket, $options);

        return $result;
    }

    /**
     * 格式化文件大小
     * @param int|string $size 文件大小（字节）
     * @return string
     */
    protected function formatSize($size): string
    {
        // 确保 $size 是数字
        $size = floatval($size);
        
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];

        for ($i = 0; $size >= 1024 && $i < 4; $i++) {
            $size /= 1024;
        }

        return round($size, 2) . ' ' . $units[$i];
    }

    /**
     * 删除多个文件
     * @param array $objects OSS存储路径列表
     * @return bool
     */
    public function deleteObjects(array $objects): bool
    {
        try {
            // 添加前缀
            if ($this->prefix) {
                $objects = array_map(function ($object) {
                    return trim($this->prefix, '/') . '/' . ltrim($object, '/');
                }, $objects);
            }

            $this->client->deleteObjects($this->bucket, $objects);
            return true;
        } catch (OssException $e) {
            return false;
        }
    }

    /**
     * 获取文件内容
     * @param string $path 文件路径
     * @param array $options 选项
     * @return string|false
     */
    public function getObjectContent($path, array $options = [])
    {
        try {
            return $this->client->getObject($this->bucket, $path, $options);
        } catch (\Exception $e) {
            return false;
        }
    }
}
